﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LxCFD
{
    public  class ScalarBase : ScalarHeadBase
    {
        public double[] x;//场
        public Vector3d[] Grad;

        protected Value1Base<double> IniValue;
        public override void CheckCreate(FvZone zone)
        {
            base.CheckCreate(zone);
            LxTool.CheckCreateArray(ref x, Cells.Length);
            LxTool.CheckCreateArray(ref Grad, InnerIndex.CellIDs.End + 1);
        }
        protected void set_x(Value1Base<double> xValue, IndexNumber indexNumber, int taskIndex)
        {
            indexNumber.CellIDs.GetSeg(out int i1, out int endp1, taskIndex);
            for (; i1 < endp1; i1++)
            {
                x[i1] = xValue.GetCellValue(i1);
            }
        }
        public void CalGradient(int taskIndex)
        {
            InnerIndex.CellIDs.GetSeg(out int i1, out int endp1, taskIndex);
            for (; i1 < endp1; i1++)
            {
                Grad[i1] = Cells[i1].CalGradient_LeastSquare(Faces, x);         
            }
        }

        public void CalBoundValue_grad0(ref FvFace face)//更准确的做法见拉普拉斯方程中的设置，这样做结果没问题，因为收敛状态修正值为0，梯度修正也为0
        {
            Vector3d v = face.Normal - face.gT * face.OToN;
            x[face.NSideCell] += ((x[face.OSideCell] - x[face.NSideCell]) * face.gT - Vector3d.DotProduct(Grad[face.OSideCell], v))
                / (face.gT + Vector3d.DotProduct(face.Normal, v) / Cells[face.OSideCell].Volumn);
        }
        protected void updateBound_grad0(IndexNumber indexNumber, int taskIndex)
        {
            indexNumber.FaceIDs.GetSeg(out int i1, out int endp1, taskIndex);
            for (; i1 < endp1; i1++)
            {
                CalBoundValue_grad0(ref Faces[i1]);
            }
        }
        protected void predict_bound_byCGrad(IndexNumber indexNumber, int taskIndex)//方法1，精度一般，计算量小，对应P603
        {
            indexNumber.FaceIDs.GetSeg(out int i1, out int endp1, taskIndex);
            for (; i1 < endp1; i1++)
            {
                ref FvFace face = ref Faces[i1];
                x[face.NSideCell] = x[face.OSideCell] + Vector3d.DotProduct(Grad[face.OSideCell], face.OToN);
            }
        }

        public bool IsEnableLimit = false;
        public double MinLim;
        public double MaxLim;
        public bool IsLimMin;
        public bool IsLimMax;
        protected static int UnnormalCount = 0;
        protected static int ExceedMaxCount = 0;
        protected static int ExceedMinCount = 0;
        public void SetLimitRule(bool isEnable, bool isLimMin, double minLim, bool isLimMax, double maxLim)
        {
            this.IsEnableLimit = isEnable;
            this.IsLimMin = isLimMin;
            this.MinLim = minLim;
            this.IsLimMax = isLimMax;
            this.MaxLim = maxLim;
        }
        private void addCount(ref int target, int addend)
        {
            if (addend == 0) return;
            ThreadsMath.AddTo(ref target, addend);
        }
        protected void limitNaN(int taskIndex)
        {
            TotalIndex.CellIDs.GetSeg(out int i1, out int endp1, taskIndex);
            int unnormal = 0;
            for (; i1 < endp1; i1++)
            {
                ref double xi = ref x[i1];
                if (double.IsNaN(xi))
                {
                    unnormal++;
                }
            }
            addCount(ref UnnormalCount, unnormal);
        }
        protected void limitMax(int taskIndex)
        {
            TotalIndex.CellIDs.GetSeg(out int i1, out int endp1, taskIndex);
            int exceedmax = 0;
            int unnormal = 0;
            for (; i1 < endp1; i1++)
            {
                ref double xi = ref x[i1];
                if (double.IsNaN(xi))
                {
                    unnormal++;
                }
                else if (xi > MaxLim)
                {
                    xi = MaxLim;
                    exceedmax++;
                }
            }
            addCount(ref UnnormalCount, unnormal);
            addCount(ref ExceedMaxCount, exceedmax);
        }
        protected void limitMin(int taskIndex)
        {
            TotalIndex.CellIDs.GetSeg(out int i1, out int endp1, taskIndex);
            int exceedmin = 0;
            int unnormal = 0;
            for (; i1 < endp1; i1++)
            {
                ref double xi = ref x[i1];
                if (double.IsNaN(xi))
                {
                    unnormal++;
                }
                else if (xi < MinLim)
                {
                    xi = MinLim;
                    exceedmin++;
                }
            }
            addCount(ref UnnormalCount, unnormal);
            addCount(ref ExceedMinCount, exceedmin);
        }
        protected void limitMinMax(int taskIndex)
        {
            TotalIndex.CellIDs.GetSeg(out int i1, out int endp1, taskIndex);
            int exceedmin = 0, exceedmax = 0;
            int unnormal = 0;
            for (; i1 < endp1; i1++)
            {
                ref double xi = ref x[i1];
                if (double.IsNaN(xi))
                {
                    unnormal++;
                }
                else if (xi < MinLim)
                {
                    xi = MinLim;
                    exceedmin++;
                }
                else if (xi > MaxLim)
                {
                    xi = MaxLim;
                    exceedmax++;
                }
            }
            addCount(ref UnnormalCount, unnormal);
            addCount(ref ExceedMinCount, exceedmin);
            addCount(ref ExceedMaxCount, exceedmax);
        }
        public void Limit_x()
        {
            if (!IsEnableLimit) return;
            if (IsLimMin)
            {
                if (IsLimMax)
                {
                    MultiThreads.RunCalTasks(limitMinMax);
                }
                else
                {
                    MultiThreads.RunCalTasks(limitMin);
                }
            }
            else
            {
                if (IsLimMax)
                {
                    MultiThreads.RunCalTasks(limitMax);
                }
                else
                {
                    MultiThreads.RunCalTasks(limitNaN);
                }
            }
            if (UnnormalCount > 0)
            {
                LxConsole.Error(string.Format($"区域{Zone.name}出现NAN值，数量{UnnormalCount}"));
                ComputeState.ReportInterrupt_FindError();
                UnnormalCount = 0;
            }
            if (ExceedMaxCount > 0)
            {
                LxConsole.Warn(string.Format($"区域{Zone.name},{ExceedMaxCount}个网格超过限制值{MaxLim}"));
                ExceedMaxCount = 0;
            }
            if (ExceedMinCount > 0)
            {
                LxConsole.Warn(string.Format($"区域{Zone.name},{ExceedMinCount}个网格超过限制值{MinLim}"));
                ExceedMinCount = 0;
            }
        }
    }
}
